/*
 * 客户端监控项实现类。
 * Copyright (C) 2009  Xubin
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.sysmo.client.item;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;
import org.mozilla.javascript.*;
import org.sysmo.communication.CommunicationInterface;
import org.sysmo.communication.CommunicationProtocol;

/**
 *监控项类主要运行两个脚本, 第一个数据采集脚本, 通过TimerTask定时执行采集数据的任务,
 * 每次采集数据后, 都要运行数据验证脚本, 判断是否产生Warning, 如果有, 将Warning添加
 * 到需要传给服务器的xml文件, 然后传送一起传送给服务器. 如果服务器暂时中断, 则将采集
 * 到的数据暂时存放到本地临时文件中, 等到服务器恢复连接后再传送给服务器.
 * @author Xubin
 */
public class Item {
    private String dataCollectScript;
    private String commandLine;
    private String dataTestScript;
    private Integer itemID;
    private long period;
    private CommunicationInterface communication;

    private TimerTask task = getNewTimerTask();
    private Timer t = new Timer();

    private TimerTask getNewTimerTask() {
        return new TimerTask() {
            public void run() {
                try {
                    File f = new File(dataCollectScript);
                    if(f.length()==0){
                        pause();
                        return;
                    }
                    HashMap<String, Integer> valueMap = doDataCollectScript(dataCollectScript, commandLine);
                    HashMap<String, String> warningMap = doDataTestScript(valueMap, dataTestScript);
                    sendToServer(valueMap, warningMap, communication);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
    }

    /**
     * 建立新的Item实例, 由客户端传入ItemID和通讯接口实例,
     * @param id 客户端中监控的每个项都有一个ID,便于管理
     * @param comm 客户端传给Item的通讯接口, Item通过通讯接口将采集到的数据和警告
     * 发送给服务器, 以及从服务器下载数据采集脚本和数据验证脚本, 并保存在本地.
     */
    public Item(Integer id, CommunicationInterface comm, boolean isNew) throws IOException {
        itemID = id;
        period = 0;

        commandLine = "";
        dataCollectScript = "" + itemID +".dataCollectScript.bat";
        dataTestScript = "" + itemID + ".dataTestScript";

        if (isNew) {
            File dcs = new File(dataCollectScript);
            if(!dcs.exists())dcs.createNewFile();

            File dts = new File(dataTestScript);
            if(!dts.exists())dts.createNewFile();
            FileOutputStream fos = new FileOutputStream(dts);
            DataOutputStream dos = new DataOutputStream(fos);
            dos.writeUTF("");
            fos.close();

        }

        communication = comm;
    }

    /**
     *建立Item时要调用start()方法, 创建一个定时任务, 以period为周期定时采集数据, 然
     * 后验证数据
     */
    public void start(){
        t.schedule(task, period, period);
    }

    /**
     *客户端获取服务器消息需要删除一个监控项时调用delete()方法
     */
    public void delete(){
        File file = new File(dataCollectScript);
        if (file.exists() && file.isFile()) {
            file.delete();
        }
        file = new File(dataTestScript);
        if (file.exists() && file.isFile()) {
            file.delete();
        }
        t.cancel();
    }

    /**
     * 让某个监控项暂停采集时客户端调用pause()方法
     */
    public void pause(){
        t.cancel();
        t = new Timer();
        task = getNewTimerTask();
    }

    /**
     * 当服务器需要改变某个监控项采集数据的周期时调用setPeriod()方法
     * @param itemperiod 客户端传入的需要更改的采集周期
     */
    public void setPeriod(long itemperiod){
        period = itemperiod;
    }

    private HashMap<String, Integer> doDataCollectScript(String dataCollectScript, String commandLine) throws IOException{
        ArrayList<String> cmdarray = new ArrayList<String>();
        String cLine = commandLine.replaceAll("\\$\\{file\\}", dataCollectScript);
        int i;
        boolean quotationFlag = false;
        StringBuilder para = new StringBuilder();
        for(i = 0; i<cLine.length(); i++){
            if(cLine.charAt(i) == '"' && quotationFlag == false){
                quotationFlag = true;
                i++;
                while(cLine.charAt(i) != '"'){
                    para.append(cLine.charAt(i++));
                }
                cmdarray.add(para.toString());
                para = new StringBuilder();
                quotationFlag= false;
            }
            else if(cLine.charAt(i) == ' '){
                cmdarray.add(para.toString());
                para = new StringBuilder();
            }
            else{
                para.append(cLine.charAt(i));
            }
        }
        if(!para.toString().isEmpty()){
            cmdarray.add(para.toString());
        }

        String[] newArray = cmdarray.toArray(new String[1]);

        HashMap<String, Integer> valueMap = new HashMap<String, Integer>();
        Process p = Runtime.getRuntime().exec(newArray);
        BufferedReader stdout = new BufferedReader(new InputStreamReader(p.getInputStream()));
        while (true) {
            String s = stdout.readLine();
            if (s == null) {
                break;
            }
            String[] value = s.split("=");
            valueMap.put(value[0], Integer.parseInt(value[1]));
        }
        return valueMap;

    }


    private HashMap<String, String> doDataTestScript(HashMap<String, Integer> valueMap, String dataTestScript) throws FileNotFoundException, IOException{
        HashMap<String, String> warningMap = new HashMap<String, String>();
        Context cx = Context.enter();
        try {
            Scriptable scope = cx.initStandardObjects();
            Warning warn = new Warning(warningMap);
            for (String key : valueMap.keySet()) {
                ScriptableObject.putProperty(scope, key, Context.javaToJS(valueMap.get(key), scope));
            }
            ScriptableObject.putProperty(scope, "warning", Context.javaToJS(warn, scope));

            File f = new File(dataTestScript);
            FileInputStream fis = new FileInputStream(f);
            DataInputStream dis = new DataInputStream(fis);
            String s = dis.readUTF();
            fis.close();

            try{
                Object result = cx.evaluateString(scope, s, "jstest", 1, null);
            }catch(Exception e){
                e.printStackTrace();
                warningMap.put(valueMap.keySet().iterator().next(), "客户端数据验证脚本出错:" + e.getMessage());
            }
        } finally {
            // Exit from the context.
            Context.exit();
        }
        return warningMap;
    }

    private void sendToServer(HashMap<String, Integer> valueMap, HashMap<String, String> warningMap, CommunicationInterface comm) throws IOException{
        HashMap<String, HashMap<String, Integer>> graphMap = new HashMap<String, HashMap<String, Integer>>();
        for (String s : valueMap.keySet()) {
            int value = valueMap.get(s);
            String[] x = s.split("__");
            if (graphMap.containsKey(x[0])) {
                graphMap.get(x[0]).put(x[1], value);
            }
            else{
                HashMap<String, Integer> tempMap = new HashMap<String, Integer>();
                tempMap.put(x[1], valueMap.get(s));
                graphMap.put(x[0], tempMap);
            }
        }
        Date time = new Date();
        StringBuilder b = new StringBuilder("<time>" + time.getTime() + "</time>");
        for(String g: graphMap.keySet()) {
            HashMap<String, Integer> valMap = graphMap.get(g);
            b.append("<graph name=\"" + g + "\">");
            for(String v: valMap.keySet()) {
                int val = valMap.get(v);
                b.append("<value name=\"" + v + "\">");
                b.append("<result>" + val + "</result>");
                if(warningMap.containsKey(g + "__" + v)){
                    b.append("<warning><![CDATA[" + warningMap.get(g + "__" + v) + "]]></warning>");
                }
                b.append("</value>");
            }
            b.append("</graph>");
        }
        if(comm.isConnected())
            comm.sendString(CommunicationProtocol.encapsulateWithItemID(14, itemID, b.toString()));
    }

//    private void saveToLocalBuffer(String content) throws IOException{
//        FileInputStream fis = new FileInputStream(localDataBuffer);
//        if(fis.available() >= 5*1024*1024){
//            localDataBuffer.delete();
//            localDataBuffer = new File("" + itemID + ".localBuffer");
//            localDataBuffer.createNewFile();
//        }
//        fis.close();
//
//        FileOutputStream fos = new FileOutputStream(localDataBuffer, true);
//        OutputStreamWriter fsw = new OutputStreamWriter(fos);
//        BufferedWriter bw = new BufferedWriter(fsw);
//        bw.write(content + "\n=====\n");
//        fos.close();
//
//    }
    
//    public void sendFileToServer(CommunicationInterface comm) throws FileNotFoundException, IOException{
//        FileInputStream fis = new FileInputStream(localDataBuffer);
//        InputStreamReader ir = new InputStreamReader(fis);
//        BufferedReader br = new BufferedReader(ir);
//
//        String s = new String();
//        while(!(s = br.readLine()).contains("=====") && s != null){
//            comm.sendString(s);
//        }
//        localDataBuffer.delete();
//        localDataBuffer.createNewFile();
//    }

    public String getCommandLine(){
        return commandLine;
    }
    public String getCollectScript(){
        return dataCollectScript;
    }

    public String getTestScript() throws IOException{
        
        File f = new File(dataTestScript);
        FileInputStream fis = new FileInputStream(f);
        DataInputStream dis = new DataInputStream(fis);
        String s = dis.readUTF();
        fis.close();
        return s;
    }

    public void setCommandLine(String cl){
        commandLine = cl;
    }

    public void setTestScript(String ts) throws IOException{
        File f = new File("" + itemID + ".dataTestScript");
        if(!f.exists()){
            f.createNewFile();
        }
        FileOutputStream fos = new FileOutputStream(f);
        DataOutputStream dos = new DataOutputStream(fos);
        dos.writeUTF(ts);
        fos.close();
    }

    public void setCollectScript(int ItemID, String postfix, int withFile) throws IOException{
        File f = new File(dataCollectScript);
        if(!f.exists()){
            f.createNewFile();
        }
        dataCollectScript = "" + ItemID + ".dataCollectScript." + postfix;
        File f2 = new File (dataCollectScript);
        f.renameTo(f2);

        if(withFile == 1){
            FileOutputStream fos = new FileOutputStream(f2);
            fos.write(communication.getByteArray());
            fos.close();
        }
    }

    public void sendCollectScript(int withFile) throws IOException{
        StringBuilder s = new StringBuilder();
        s.append("<commandLine><![CDATA[" + commandLine + "]]></commandLine>");
        String[] x = dataCollectScript.split("\\.");
        s.append("<postfix>" + x[2] + "</postfix>");
        s.append("<withFile>" + withFile + "</withFile>");
        communication.sendString(CommunicationProtocol.encapsulateWithItemID(11, itemID, s.toString()));

        if(withFile == 1){
            File f = new File(dataCollectScript);
            FileInputStream fis = new FileInputStream(f);
            int byteno = fis.available();
            byte[] data = new byte[byteno];
            fis.read(data);
            fis.close();
            communication.sendByteArray(data);
        }
    }

    public void sendTestScript() throws IOException{
        File f = new File(dataTestScript);
        FileInputStream fis = new FileInputStream(f);
        DataInputStream dis = new DataInputStream(fis);
        String s = dis.readUTF();
        fis.close();
        
        StringBuilder sb = new StringBuilder();
        sb.append("<script><![CDATA[" + s + "]]></script>");
        communication.sendString(CommunicationProtocol.encapsulateWithItemID(12, itemID, sb.toString()));
    }

    public Integer getItemID(){
        return itemID;
    }

    public long getPeriod(){
        return period;
    }

}
